﻿#region File Description
//-----------------------------------------------------------------------------
// Game1.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.IO;

namespace FirstPersonCamera
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>

    public class BoundingBoxObject
    {
        public string type;
        public BoundingBox boundingBox;
        public BoundingBoxObject(string type, BoundingBox boundingBox)
        {
            this.type = type;
            this.boundingBox = boundingBox;
        }
    }

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        Matrix view;
        Matrix proj;
        Matrix world;

        Model box;
        Texture2D boxTexture;
        Texture2D texture2;
        GameTextureContainer gameTextureContainer;

        BasicEffect basicEffect;
        //VertexPositionColor[] verticesColor;

        List<VertexPositionTexture> verticesTextureList;
        //VertexPositionTexture[] verticesTexture;

        VertexDeclaration vDeclColor;
        VertexDeclaration vDeclTexture;

        VertexBuffer groundVertexBuffer;

        BoundingBox aBox;
        BoundingBoxObject bBox;


        BoundingBoxObject[] wallBoundingBoxes;
        BoundingSphere pacmanSphere;

        enum CollisionType { None, GameBall, Ghost, BoxTop, BoxRight, BoxLeft, BoxFront, BoxBack, BoxOutUL, BoxOutLL, BoxOutUR, BoxOutLR, BoxInUL, BoxInLL, BoxInUR, BoxInLR, Boundary}


        // Set the avatar position and rotation variables.
        Vector3 avatarPosition = new Vector3(0, 0, 0);
        Vector3 avatarVelocity = new Vector3(0, 0, 0);
        Vector3 avatarAccel = new Vector3(0, -.03f, 0);

        Vector3 sideVector;

        float avatarYaw;
        float avatarPitch = MathHelper.PiOver2;

        DateTime startedVibration;
        TimeSpan vibrateDuration = new TimeSpan((int)(System.TimeSpan.TicksPerSecond * GameConstants.vibrationDuration));

        bool vibrating;
        bool inAir = false;
        bool wasInAir = false;

        // Set the direction the camera points without rotation.
        Vector3 cameraReference = new Vector3(0, 0, 1);
        Vector3 cameraDirection;

        GraphicsDeviceManager graphics;
        char[,] floorPlan;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            InitializeGameTextureContainer();
            initializeFloorPlan();
            InitializeQuad();
            InitializeBasicEffect();
            
            base.Initialize();
        }

        protected void InitializeGameTextureContainer()
        {
            gameTextureContainer = new GameTextureContainer(4, 1);
        }

        protected void InitializeBasicEffect()
        {
            basicEffect = new BasicEffect(graphics.GraphicsDevice, null);
            basicEffect.Alpha = 1.0f;
            basicEffect.DiffuseColor = new Vector3(1f, 1f, 1f);
            basicEffect.SpecularColor = new Vector3(1f, 1f, 1f);
            basicEffect.SpecularPower = 5.0f;
            basicEffect.AmbientLightColor = new Vector3(1f, 1f, 1f);

            basicEffect.DirectionalLight0.Enabled = true;
            basicEffect.DirectionalLight0.DiffuseColor = Vector3.One;
            basicEffect.DirectionalLight0.Direction =
                Vector3.Normalize(new Vector3(1.0f, -1.0f, -1.0f));
            basicEffect.DirectionalLight0.SpecularColor = Vector3.One;

            basicEffect.DirectionalLight1.Enabled = true;
            basicEffect.DirectionalLight1.DiffuseColor =
                new Vector3(1f, 1f, 1f);
            basicEffect.DirectionalLight1.Direction =
                Vector3.Normalize(new Vector3(-1.0f, -1.0f, 1.0f));
            basicEffect.DirectionalLight1.SpecularColor =
                new Vector3(1f, 1f, 1f);

            basicEffect.LightingEnabled = true;
            
            basicEffect.World = world;
            basicEffect.View = view;
            basicEffect.Projection = proj;
            basicEffect.CommitChanges();
        }

        protected void initializeFloorPlan()
        {
            TextReader fs = new StreamReader(new FileStream("Level1.txt", FileMode.OpenOrCreate, FileAccess.Read), System.Text.Encoding.Unicode);
            String wholeLevel = fs.ReadToEnd();
            
            wholeLevel = wholeLevel.Replace("\r\n", "\n");
            char[] splitOver = {'\n'};

            String[] levelSplit = wholeLevel.Split(splitOver);


            char[] splitTest = levelSplit[0].ToCharArray();
            int cols = splitTest.Length;
            int rows = levelSplit.Length;

            floorPlan = new char[rows,cols];

            for (int a = 0; a < rows; a++)
            {
                char[] charArray = levelSplit[a].ToCharArray();
                for (int b = 0;b<cols;b++)
                {
                    floorPlan[a, b] = charArray[b];
                }                
            }
        }
        protected void InitializeQuad()
        {
            int surfaceWidth = floorPlan.GetLength(1) + 1;
            int surfaceHeight = floorPlan.GetLength(0) + 1;

            float o = 0;
            float o2 = 0;
            
            verticesTextureList = new List<VertexPositionTexture> ();
            //verticesTexture = new VertexPositionTexture[6];

            //Quads for ground surface
            for (int i = 0; i < surfaceWidth; i++)
            {
                o2 = 0;
                for (int j = 0; j < surfaceHeight; j++)
                {
                    addTexturedQuad(new Vector3(-10f + o, 0f, 10f + o2), new Vector3(1, 0, 0), new Vector3(0, 0, 1), 20f, 20f, gameTextureContainer.getTexture(0, 0), "whole", verticesTextureList);
                    o2 += 20;
                }
                o += 20;
            }
            List<BoundingBoxObject> bbList = new List<BoundingBoxObject>();
            
            for (int a = 0; a < floorPlan.GetLength(0); a++)
            {
                for (int b = 0; b < floorPlan.GetLength(1); b++)
                {
                    Vector3[] wallPoints = new Vector3[2];

                    switch (floorPlan[a, b])
                    {
                        case '┬':

                            addTexturedQuad(new Vector3((b * 20), GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth, GameConstants.fenceHeight, gameTextureContainer.getTexture(1, 0), "whole", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20), GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, -1), GameConstants.fenceWidth, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "upperHalf", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("front", aBox);

                            bbList.Add(bBox);
                            break;

                        case '┴':

                            addTexturedQuad(new Vector3((b * 20), GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth, GameConstants.fenceHeight, gameTextureContainer.getTexture(1, 0), "whole", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20), GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, 1), GameConstants.fenceWidth, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "lowerHalf", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("back", aBox);

                            bbList.Add(bBox);                           
                            break;
                            
                        case '├':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20)), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth, GameConstants.fenceHeight, gameTextureContainer.getTexture(1, 0), "whole", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20)), new Vector3(0, 0, 1), new Vector3(-1, 0, 0), GameConstants.fenceWidth, GameConstants.fenceWidth/2, gameTextureContainer.getTexture(2, 0), "upperHalf", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("right", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '┤':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20)), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth, GameConstants.fenceHeight, gameTextureContainer.getTexture(1, 0), "whole", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10 - (GameConstants.fenceWidth / 2), GameConstants.fenceHeight, (a * 20)), new Vector3(0, 0, 1), new Vector3(-1, 0, 0), GameConstants.fenceWidth, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "lowerHalf", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("left", aBox);

                            bbList.Add(bBox);  
                            break;
                        
                        case '┌':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, 1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("outsideUL", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '└':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, -1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("outsideLL", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '╛':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("insideLR", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '╕':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 0, -1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("insideUR", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '╒':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(-1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("insideUL", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '╘':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, 1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("insideLL", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '┘':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, -1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("outsideLR", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '┐':

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, 1), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "leftHalf", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), GameConstants.fenceWidth / 2, GameConstants.fenceHeight, gameTextureContainer.getTexture(3, 0), "rightHalf", verticesTextureList);

                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(1, 0, 0), new Vector3(0, 0, -1), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);
                            addTexturedQuad(new Vector3((b * 20) + 10, GameConstants.fenceHeight, (a * 20) + 10), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), GameConstants.fenceWidth / 2, GameConstants.fenceWidth / 2, gameTextureContainer.getTexture(2, 0), "quarter", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("outsideUR", aBox);

                            bbList.Add(bBox);  
                            break;

                        case '┼':
                            addTexturedQuad(new Vector3((b * 20), GameConstants.fenceHeight, (a * 20)), new Vector3(0, 0, 1), new Vector3(-1, 0, 0), GameConstants.fenceWidth, GameConstants.fenceWidth, gameTextureContainer.getTexture(2, 0), "whole", verticesTextureList);

                            wallPoints[0] = new Vector3(b * 20f, 0f, a * 20f);
                            wallPoints[1] = new Vector3((b + 1f) * 20f, GameConstants.fenceHeight, (a + 1f) * 20f);

                            aBox = BoundingBox.CreateFromPoints(wallPoints);
                            bBox = new BoundingBoxObject("top", aBox);

                            bbList.Add(bBox);
                            break;

                        case '☺':
                            avatarPosition = new Vector3(b * 20f, 0f, a * 20f);
                            break;                      
                    }
                }
                wallBoundingBoxes = bbList.ToArray();
            }
            groundVertexBuffer = new VertexBuffer(GraphicsDevice, verticesTextureList.Count * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);

            groundVertexBuffer.SetData<VertexPositionTexture>(verticesTextureList.ToArray());

            vDeclColor = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
            vDeclTexture = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);      
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {

            box = Content.Load<Model>("cube");
            boxTexture = Content.Load<Texture2D>("Picture 2");
            texture2 = Content.Load<Texture2D>("rock");
            gameTextureContainer.setTexture(texture2);
            //texture3 = Content.Load<Texture2D>("rock");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            UpdateAvatarPosition(gameTime);
            UpdateCamera();

            basicEffect.World = world;
            basicEffect.View = view;
            basicEffect.Projection = proj;
            basicEffect.CommitChanges();

            base.Update(gameTime);
        }

        /// <summary>
        /// Updates the position and direction of the avatar.
        /// </summary>
        void UpdateAvatarPosition(GameTime gameTime)
        {
            GamePadState currentState = GamePad.GetState(PlayerIndex.One);
            KeyboardState keyboardState = Keyboard.GetState();

            pacmanSphere = new BoundingSphere(avatarPosition, 0.04f);

            updateUsingKeyboard(gameTime);

            if (currentState.IsConnected)
                updateUsingController(gameTime);

            if (avatarPitch > MathHelper.Pi - .01f)
                avatarPitch = MathHelper.Pi - .01f;
            if (avatarPitch < 0)
                avatarPitch = 0;

            CollisionType collision = CheckCollision(pacmanSphere);

            if (!avatarWithinBounds() || (avatarPosition.Y > 0 && collision == CollisionType.None)) //Out of bounds or in air w/ no collision
                avatarVelocity += avatarAccel;

            else if (collision == CollisionType.BoxFront)
            {
                if (avatarPosition.Y < GameConstants.fenceHeight)
                {
                    avatarVelocity += avatarAccel;
                    if(avatarVelocity.Z > 0)
                        avatarVelocity.Z = 0f;
                }
            }

            else if (collision == CollisionType.BoxBack)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxLeft)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxRight)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxOutUL)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxOutLL)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxOutUR)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxOutLR)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxInUL)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxInLL)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxInUR)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxInLR)
                avatarPosition.Y = GameConstants.fenceHeight;

            else if (collision == CollisionType.BoxTop)
                avatarPosition.Y = GameConstants.fenceHeight;

            else
            {
                avatarPosition.Y = 0;
                avatarVelocity.Y = 0;
            }

            avatarPosition += avatarVelocity;

            //Reset button!
            if (currentState.Buttons.B == ButtonState.Pressed || keyboardState.IsKeyDown(Keys.R))
            {
                avatarPosition = new Vector3(0f, 0f, 0f);
                avatarVelocity = new Vector3(0f, 0f, 0f);
            }
        }

        protected void updateUsingKeyboard(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                avatarYaw += GameConstants.rotationSpeedX;
            if (keyboardState.IsKeyDown(Keys.Right))
                avatarYaw -= GameConstants.rotationSpeedX;
            if (keyboardState.IsKeyDown(Keys.Down))
                avatarPitch += GameConstants.rotationSpeedY;
            if (keyboardState.IsKeyDown(Keys.Up))
                avatarPitch -= GameConstants.rotationSpeedY;

            CollisionType c = CheckCollision(pacmanSphere);
            CollisionType[] d = { CollisionType.BoxFront, CollisionType.BoxBack, CollisionType.BoxLeft, CollisionType.BoxRight, CollisionType.BoxOutUL, CollisionType.BoxOutLL, CollisionType.BoxOutUR, CollisionType.BoxOutLR, CollisionType.BoxInUL, CollisionType.BoxInLL, CollisionType.BoxInUR, CollisionType.BoxInLR, CollisionType.BoxTop };


            if (avatarPosition.Y == 0 || c == d[0] || c == d[1] || c == d[2] || c == d[3] || c == d[4] || c == d[5] || c == d[6] || c == d[7] || c == d[8] || c == d[9] || c == d[10] || c == d[11] || c == d[12])
            {
                avatarVelocity = Vector3.Zero;
                Matrix rotationMatrix = Matrix.CreateRotationY(avatarYaw);

                Vector3 forwardMagnitude = Vector3.Zero;
                
                //= new Vector3(0, 0, currentState.ThumbSticks.Left.Y * forwardSpeed);
                

                if (keyboardState.IsKeyDown(Keys.W))
                    forwardMagnitude += new Vector3(0, 0, GameConstants.forwardSpeed);
                if (keyboardState.IsKeyDown(Keys.S))
                    forwardMagnitude += new Vector3(0, 0, -GameConstants.forwardSpeed);
                if (keyboardState.IsKeyDown(Keys.A))
                    forwardMagnitude += new Vector3(GameConstants.forwardSpeed, 0, 0);
                if (keyboardState.IsKeyDown(Keys.D))
                    forwardMagnitude += new Vector3(-GameConstants.forwardSpeed, 0, 0);

                Vector3 forwardVector = Vector3.Transform(forwardMagnitude, rotationMatrix);
                avatarVelocity += forwardVector;

                if (keyboardState.IsKeyDown(Keys.Space))
                {
                    avatarVelocity.Y = GameConstants.jumpVelocity;
                    avatarPosition.Y = 0.001f;
                }



            }

        }
        protected void updateUsingController(GameTime gameTime)
        {
            
            GamePadState currentState = GamePad.GetState(PlayerIndex.One);
            if (vibrating && DateTime.Now.Subtract(startedVibration) > vibrateDuration)
            {
                GamePad.SetVibration(PlayerIndex.One, 0, 0);
                vibrating = false;
            }

            avatarYaw -= (currentState.ThumbSticks.Right.X * GameConstants.rotationSpeedX);
            avatarPitch -= (currentState.ThumbSticks.Right.Y * GameConstants.rotationSpeedY);

            if (avatarPosition.Y == 0 && avatarWithinBounds())
            {
                avatarVelocity = Vector3.Zero;
                //Scaled forward motion
                Matrix rotationMatrix = Matrix.CreateRotationY(avatarYaw);

                Vector3 forwardMagnitude = new Vector3(0, 0, currentState.ThumbSticks.Left.Y * GameConstants.forwardSpeed);
                Vector3 forwardVector = Vector3.Transform(forwardMagnitude, rotationMatrix);

                avatarVelocity += forwardVector;

                //Scaled side motion
                Vector3 sideMagnitude = new Vector3(-currentState.ThumbSticks.Left.X * GameConstants.forwardSpeed, 0, 0);
                sideVector = Vector3.Transform(sideMagnitude, rotationMatrix);

                avatarVelocity += sideVector;

                if (currentState.Buttons.A == ButtonState.Pressed)
                {
                    avatarVelocity.Y = GameConstants.jumpVelocity;
                    avatarPosition.Y = 0.001f;
                }
            }

            wasInAir = inAir;
            if (avatarPosition.Y > 0)
                inAir = true;
            else
                inAir = false;

            if (wasInAir && !inAir)
            {
                startedVibration = DateTime.Now;
                vibrating = true;
                GamePad.SetVibration(PlayerIndex.One, 1, 1);
            }
        }


        /// <summary>
        /// Updates the position and direction of the camera relative to the avatar.
        /// </summary>
        void UpdateCamera()
        {

            // Calculate the camera's current position.
            Vector3 cameraPosition = avatarPosition;
            cameraPosition.Y += 14f;
            cameraDirection = Vector3.Zero;

            cameraDirection.Z = (float)(Math.Sin(avatarPitch) * Math.Cos(avatarYaw));
            cameraDirection.Y = (float)Math.Cos(avatarPitch);
            cameraDirection.X = (float)(Math.Sin(avatarPitch) * Math.Sin(avatarYaw));

            // Set up the view matrix and projection matrix.
            view = Matrix.CreateLookAt(cameraPosition, cameraPosition + cameraDirection, new Vector3(0.0f, 1.0f, 0.0f));

            world = Matrix.Identity;

            Viewport viewport = graphics.GraphicsDevice.Viewport;
            float aspectRatio = (float)viewport.Width / (float)viewport.Height;

            proj = Matrix.CreatePerspectiveFieldOfView(GameConstants.viewAngle, aspectRatio, GameConstants.nearClip, GameConstants.farClip);
        }

        private CollisionType CheckCollision(BoundingSphere sphere)
        {
            string[] boundingBoxType = {"front","back","right","left","outsideUL","outsideLL","insideLR","insideUR","insideUL","insideLL","outsideLR","OutsideUR","top"};

            for (int i = 0; i < wallBoundingBoxes.Length; i++)
            {
                if (wallBoundingBoxes[i].boundingBox.Contains(sphere) !=  ContainmentType.Disjoint)
                {
                    if (wallBoundingBoxes[i].type == boundingBoxType[0])
                        return CollisionType.BoxFront;

                    if (wallBoundingBoxes[i].type == boundingBoxType[1])
                        return CollisionType.BoxBack;

                    if (wallBoundingBoxes[i].type == boundingBoxType[2])
                        return CollisionType.BoxRight;

                    if (wallBoundingBoxes[i].type == boundingBoxType[3])
                        return CollisionType.BoxLeft;

                    if (wallBoundingBoxes[i].type == boundingBoxType[4])
                        return CollisionType.BoxOutUL;

                    if (wallBoundingBoxes[i].type == boundingBoxType[5])
                        return CollisionType.BoxOutLL;

                    if (wallBoundingBoxes[i].type == boundingBoxType[6])
                        return CollisionType.BoxInLR;

                    if (wallBoundingBoxes[i].type == boundingBoxType[7])
                        return CollisionType.BoxInUR;

                    if (wallBoundingBoxes[i].type == boundingBoxType[8])
                        return CollisionType.BoxInUL;

                    if (wallBoundingBoxes[i].type == boundingBoxType[9])
                        return CollisionType.BoxInLL;

                    if (wallBoundingBoxes[i].type == boundingBoxType[10])
                        return CollisionType.BoxOutLR;

                    if (wallBoundingBoxes[i].type == boundingBoxType[11])
                        return CollisionType.BoxOutUR;

                    if (wallBoundingBoxes[i].type == boundingBoxType[12])
                        return CollisionType.BoxTop;
                }
                    
            }

            return CollisionType.None;
        }

        /// <summary>
        /// Draws the box model; a reference point for the avatar.
        /// </summary>
        void DrawModel(Model model, Matrix world, Texture2D texture)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect be in mesh.Effects)
                {
                    be.Projection = proj;
                    be.View = view;
                    be.World = world;
                    be.Texture = texture;
                    be.TextureEnabled = true;
                }
                mesh.Draw();
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.RenderState.CullMode = CullMode.None;
            graphics.GraphicsDevice.Clear(Color.SteelBlue);
            //graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            basicEffect.Begin();

            DrawModel(box, Matrix.Identity, boxTexture);

            /*basicEffect.VertexColorEnabled = true;
            basicEffect.TextureEnabled = false;
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                GraphicsDevice.VertexDeclaration = vDeclColor;
                GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, verticesColor, 0, 2);

                pass.End();
            }*/

            basicEffect.VertexColorEnabled = false;
            basicEffect.TextureEnabled = true;
            basicEffect.Texture = texture2;

            
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                GraphicsDevice.VertexDeclaration = vDeclTexture;
                GraphicsDevice.Vertices[0].SetSource(groundVertexBuffer, 0, VertexPositionTexture.SizeInBytes);
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (verticesTextureList.Count/ 3));

                pass.End();
            }

            basicEffect.End();

            base.Draw(gameTime);
        }

        protected bool avatarWithinBounds()
        {
            return avatarPosition.X >= 0 && avatarPosition.X <= floorPlan.GetLength(1) * 20 && avatarPosition.Z >= 0 && avatarPosition.Z <= floorPlan.GetLength(0) * 20;
        }

        protected void addTexturedQuad(Vector3 referenceVertex, Vector3 widthDir, Vector3 heightDir, float width, float height, GameTexture tex, string texState, List<VertexPositionTexture> vertexList)
        {
            tex.setState(texState);

            vertexList.Add(new VertexPositionTexture(referenceVertex, tex.UpperLeft()));
            vertexList.Add(new VertexPositionTexture(referenceVertex + width * widthDir, tex.UpperRight()));
            vertexList.Add(new VertexPositionTexture(referenceVertex - height * heightDir, tex.LowerLeft()));
            vertexList.Add(new VertexPositionTexture(referenceVertex + width * widthDir, tex.UpperRight()));
            vertexList.Add(new VertexPositionTexture(referenceVertex - height * heightDir, tex.LowerLeft()));
            vertexList.Add(new VertexPositionTexture(referenceVertex + (width * widthDir) - (height * heightDir), tex.LowerRight()));
        }
    }
}